import java.io.*;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;

public class ColorHalftone2 {
	public BufferedImage image;
	public int[][] rArray, gArray, bArray;

	public static void main(String[] args) {
		ColorHalftone2 ch = new ColorHalftone2();
		ch.readImageToArrays(args[0]);
		ch.doErrorDiffusion(ch.rArray);
		ch.doErrorDiffusion(ch.gArray);
		ch.doErrorDiffusion(ch.bArray);
		ch.combineRGB();
		ch.writeImage(args[1]);
	}

	public void readImageToArrays(String fileName) {
		try {
			image = ImageIO.read(new File(fileName));
			rArray = new int[image.getHeight()][image.getWidth()];
			gArray = new int[image.getHeight()][image.getWidth()];
			bArray = new int[image.getHeight()][image.getWidth()];
			// copy original RGB into arrays
			for (int i = 0; i < image.getHeight(); i++) {
				for (int j = 0; j < image.getWidth(); j++) {
					int pixel = image.getRGB(j, i);
					rArray[i][j] = (pixel & 0x00ff0000) >> 16;
					gArray[i][j] = (pixel & 0x0000ff00) >> 8;
					bArray[i][j] = (pixel & 0x000000ff);
				}
			}

			/*
			 * BufferedWriter out = new BufferedWriter(new
			 * OutputStreamWriter(new FileOutputStream( "RESULT.txt"))); int
			 * pixel, lmn; for (int i = 0; i < image.getHeight(); i++) { for
			 * (int j = 0; j < image.getWidth(); j++) { System.out.println(i +
			 * ", " + j); pixel = image.getRGB(j, i); lmn = (int) (((pixel &
			 * 0x00ff0000) >> 16) * 0.3 + ((pixel & 0x0000ff00) >> 8) 0.59 +
			 * (pixel & 0x000000ff) * 0.11); if (lmn < 50) { out.write("@"); }
			 * else if (lmn < 100) { out.write("&"); } else if (lmn < 150) {
			 * out.write("*"); } else if (lmn < 200) { out.write(";"); } else {
			 * out.write("."); } } out.newLine(); } out.close();
			 */
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void doErrorDiffusion(int[][] array) {
		int error;
		for (int i = 0; i < array.length; i++) {
			if (i % 2 == 0) {
				for (int j = 0; j < array[i].length; j++) {
					// compute error value
					if (array[i][j] > 128) {
						error = array[i][j] - 255;
					} else {
						error = array[i][j];
					}
					// left to right diffusion
					if (j + 1 < array[i].length) {
						array[i][j + 1] = checkValue(array[i][j + 1],
								error * 7 / 16);
					}
					if (i + 1 < array.length && j - 1 >= 0) {
						array[i + 1][j - 1] = checkValue(array[i + 1][j - 1],
								error * 3 / 16);
					}
					if (i + 1 < array.length) {
						array[i + 1][j] = checkValue(array[i + 1][j],
								error * 5 / 16);
					}
					if (i + 1 < array.length && j + 1 < array[i].length) {
						array[i + 1][j + 1] = checkValue(array[i + 1][j + 1],
								error * 1 / 16);
					}
				}
			} else {
				for (int j = array[i].length - 1; j >= 0; j--) {
					// compute error value
					if (array[i][j] > 128) {
						error = array[i][j] - 255;
					} else {
						error = array[i][j];
					}
					// left to right diffusion
					if (j - 1 >= 0) {
						array[i][j - 1] = checkValue(array[i][j - 1],
								error * 7 / 16);
					}
					if (i + 1 < array.length && j - 1 >= 0) {
						array[i + 1][j - 1] = checkValue(array[i + 1][j - 1],
								error * 1 / 16);
					}
					if (i + 1 < array.length) {
						array[i + 1][j] = checkValue(array[i + 1][j],
								error * 5 / 16);
					}
					if (i + 1 < array.length && j + 1 < array[i].length) {
						array[i + 1][j + 1] = checkValue(array[i + 1][j + 1],
								error * 3 / 16);
					}
				}
			}
		}
		// truncate to 0 and 255
		for (int i = 0; i < array.length; i++) {
			for (int j = 0; j < array[i].length; j++) {
				if (array[i][j] > 128) {
					array[i][j] = 255;
				} else {
					array[i][j] = 0;
				}
			}
		}

	}

	public int checkValue(int pixelValue, int errorValue) {
		if (pixelValue + errorValue < 0) {
			return 0;
		} else if (pixelValue + errorValue > 255) {
			return 255;
		} else {
			return pixelValue + errorValue;
		}
	}

	public void combineRGB() {
		for (int i = 0; i < image.getHeight(); i++) {
			for (int j = 0; j < image.getWidth(); j++) {
				image.setRGB(j, i, (rArray[i][j] << 16) | (gArray[i][j] << 8)
						| bArray[i][j]);
			}
		}
	}

	public void writeImage(String fileName) {
		try {
			ImageIO.write(image, "bmp", new File(fileName + ".bmp"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
